#include "xmlrpc_support.h"


IFeedReaderEngine* FeedReaderXmlRpcSupport::Engine = NULL;


FeedReaderXmlRpcSupport::FeedReaderXmlRpcSupport ()
{
}


#define REGISTER_METHOD(name,fn) \
	xmlrpc_registry_add_method \
	(	\
		&env,	\
		registryP,	\
		NULL,	\
		name,	\
		fn,	\
		NULL	\
	);

#define EXIT_IF_BAD_PARAMS if ( env->fault_occurred ) return NULL


void FeedReaderXmlRpcSupport::Run (IFeedReaderEngine* engine, int port)
{
	if ( Engine != NULL )
		throw new AlreadyRunningException ();
	
	Engine = engine;
	
	xmlrpc_server_abyss_parms serverparm;
	xmlrpc_registry* registryP;
	xmlrpc_env env;

	xmlrpc_env_init(&env);
	registryP = xmlrpc_registry_new(&env);

	REGISTER_METHOD("feedreader.gettags",&GetTags);
	REGISTER_METHOD("feedreader.getfeeds",&GetFeeds);
	REGISTER_METHOD("feedreader.getfeedbynumber", &GetFeedByNumber);
	REGISTER_METHOD("feedreader.getnews", &GetNews);

	REGISTER_METHOD("feedreader.addtag", &AddTag);
	REGISTER_METHOD("feedreader.deltag", &DelTag);
	
	REGISTER_METHOD("feedreader.addfeed", &AddFeed);
	REGISTER_METHOD("feedreader.delfeed", &DelFeed);

	REGISTER_METHOD("feedreader.assigntags", &AssignTags);
	REGISTER_METHOD("feedreader.removetags", &RemoveTags);

	REGISTER_METHOD("feedreader.search", &Search);

	serverparm.config_file_name     = NULL;
	serverparm.registryP            = registryP;
	serverparm.port_number          = port;
	serverparm.log_file_name        = "/tmp/xmlrpc.log";
				
	// Nunca retorna...
	xmlrpc_server_abyss(&env, &serverparm, XMLRPC_APSIZE(log_file_name)); 
}

void FeedReaderXmlRpcSupport::Stop ()
{
	if ( Engine == NULL )
		throw new NotRunningException ();

	Engine = NULL;
	// TODO: Apagar el servidor
}

FR_RPC	FeedReaderXmlRpcSupport::GetTags FR_RPC_PARAMS
{
	set<__Tag*> tags = Engine->GetTags ();
	
	xmlrpc_value* retval = xmlrpc_array_new (env);
	xmlrpc_value* tmp;

	set<__Tag*>::iterator iter;
	for ( iter = tags.begin() ; iter != tags.end() ; iter++ )
	{
		tmp = xmlrpc_build_value(env, "{s:i,s:s}", 
				"TagID", (*iter)->TagID, 
				"Name", (*iter)->Name.c_str() );
		xmlrpc_array_append_item(env, retval, tmp);
		delete (*iter);
	}
	
	return retval;
}


FR_RPC  FeedReaderXmlRpcSupport::GetFeeds FR_RPC_PARAMS
{
	set<__Feed*> feeds = Engine->GetFeeds ();

	xmlrpc_value* retval = xmlrpc_array_new (env);
	xmlrpc_value* tmp;

	set<__Feed*>::iterator iter;
	for ( iter = feeds.begin() ; iter != feeds.end() ; iter++ )
	{
		tmp = xmlrpc_build_value(env, "{s:i,s:s,s:s,s:i}", 
				"FeedID", (*iter)->FeedID, 
				"Name", (*iter)->Name.c_str(),
				"Url", (*iter)->Url.c_str(),
				"DateTime", (*iter)->DateTime );
		
		xmlrpc_array_append_item(env, retval, tmp);
		delete (*iter);
	}
	
	return retval;
}


FR_RPC 	FeedReaderXmlRpcSupport::GetFeedByNumber FR_RPC_PARAMS
{
	xmlrpc_int32 feedID;
	xmlrpc_int32 start;
	xmlrpc_int32 count;
	xmlrpc_decompose_value(env, param_array, "(iii)", &feedID,&start,&count);
	EXIT_IF_BAD_PARAMS;
	
	list<__News*> news = Engine->GetFeedByNumber ( feedID , start , count );
	return MakeNewsArray(env,news);
}


xmlrpc_value* FeedReaderXmlRpcSupport::MakeNewsArray(xmlrpc_env* env,list<__News*>& news)
{
	xmlrpc_value* retval = xmlrpc_array_new (env);
	xmlrpc_value* tmp;

	list<__News*>::iterator iterNews;
	for ( iterNews = news.begin() ; iterNews != news.end() ; iterNews++ )
	{
		xmlrpc_value* tags = xmlrpc_array_new(env);
		xmlrpc_value* tmp;

		set<int>::iterator iter;
		for ( iter = (*iterNews)->Tags.begin() ; iter != (*iterNews)->Tags.end() ; iter++ )
		{
			tmp = xmlrpc_build_value(env, "i", *iter);
			xmlrpc_array_append_item(env, tags, tmp);
		}
		
		tmp = xmlrpc_build_value(env, "{s:i,s:i,s:s,s:s,s:s,s:s,s:i,s:A}", 
			"FeedID", (*iterNews)->FeedID,
			"NewsID", (*iterNews)->NewsID, 
			"Title", (*iterNews)->Title.c_str(),
			"Description", (*iterNews)->Description.c_str(),
			"Author", (*iterNews)->Author.c_str(),
			"Url", (*iterNews)->Url.c_str(),
			"DateTime", (*iterNews)->DateTime ,
			"Tags", tags);

		xmlrpc_array_append_item(env, retval, tmp);
		delete (*iterNews);
	}

	return retval;
}

FR_RPC	FeedReaderXmlRpcSupport::GetNews FR_RPC_PARAMS
{
	xmlrpc_int32 page, count;
	xmlrpc_decompose_value(env, param_array, "(ii)", &page, &count);
	EXIT_IF_BAD_PARAMS;

	list<__News*> news = Engine->GetNews ( page , count );
	return MakeNewsArray(env,news);
}


FR_RPC	FeedReaderXmlRpcSupport::AddTag FR_RPC_PARAMS
{
	char* name;
	xmlrpc_decompose_value(env, param_array, "(s)", &name);
	EXIT_IF_BAD_PARAMS;
	
	string _name = name;
	int retval = Engine->AddTag ( _name );

	return xmlrpc_build_value(env, "i", retval);
}


FR_RPC	FeedReaderXmlRpcSupport::DelTag FR_RPC_PARAMS
{
	xmlrpc_int32 tagID;
	xmlrpc_decompose_value(env, param_array, "(i)", &tagID);
	EXIT_IF_BAD_PARAMS;

	int retval = Engine->DelTag ( tagID );

	return xmlrpc_build_value(env, "i", retval);
}


FR_RPC	FeedReaderXmlRpcSupport::AddFeed FR_RPC_PARAMS
{
	char* name;
	char* url;
	xmlrpc_decompose_value(env, param_array, "(ss)", &name, &url);
	EXIT_IF_BAD_PARAMS;
	
	string _name = name;
	string _url = url;
	int retval = Engine->AddFeed ( _name , _url );

	return xmlrpc_build_value(env, "i", retval);
}


FR_RPC	FeedReaderXmlRpcSupport::DelFeed FR_RPC_PARAMS
{
	xmlrpc_int32 feedID;
	xmlrpc_decompose_value(env, param_array, "(i)", &feedID);
	EXIT_IF_BAD_PARAMS;

	int retval = Engine->DelFeed ( feedID );

	return xmlrpc_build_value(env, "i", retval);
}


set<int> FeedReaderXmlRpcSupport::MakeIntSet(xmlrpc_env* env,xmlrpc_value* param)
{
	set<int> retval;

	for ( int i = 0 ; i < xmlrpc_array_size(env,param); i++ )
	{
		xmlrpc_value* tmp;
		xmlrpc_int32 tmp2;
		
		xmlrpc_array_read_item(env, param, i, &tmp);
		xmlrpc_decompose_value(env, tmp, "i", &tmp2);
		retval.insert((int) tmp2);
	}

	return retval;
}


FR_RPC	FeedReaderXmlRpcSupport::AssignTags FR_RPC_PARAMS
{
	xmlrpc_int32 feedID;
	xmlrpc_int32 newsID;
	xmlrpc_value* tagArray;
	xmlrpc_decompose_value(env, param_array, "(iiA)", &feedID, &newsID, &tagArray);
	EXIT_IF_BAD_PARAMS;

	set<int> tags = MakeIntSet(env,tagArray);
	
	int retval = Engine->AssignTags ( feedID , newsID , tags );
	return xmlrpc_build_value(env, "i", retval);
}


FR_RPC	FeedReaderXmlRpcSupport::RemoveTags FR_RPC_PARAMS
{
	xmlrpc_int32 feedID;
	xmlrpc_int32 newsID;
	xmlrpc_value* tagArray;
	xmlrpc_decompose_value(env, param_array, "(iiA)", &feedID , &newsID, &tagArray);
	EXIT_IF_BAD_PARAMS;

	set<int> tags = MakeIntSet(env,tagArray);
	
	int retval = Engine->RemoveTags ( feedID , newsID , tags );
	return xmlrpc_build_value(env, "i", retval);
}


FR_RPC	FeedReaderXmlRpcSupport::Search FR_RPC_PARAMS
{
	xmlrpc_value* feedsArray;
	xmlrpc_value* positiveArray;
	xmlrpc_value* negativeArray;
	xmlrpc_int32 from;
	xmlrpc_int32 to;
	xmlrpc_decompose_value(env, param_array, "(AAAii)", &feedsArray, &positiveArray,
					&negativeArray, &from ,&to);
	EXIT_IF_BAD_PARAMS;

	set<int> feeds 		= MakeIntSet(env,feedsArray);
	set<int> positive	= MakeIntSet(env,positiveArray);
	set<int> negative	= MakeIntSet(env,negativeArray);
		
	list<__News*> news = Engine->Search ( feeds , positive , negative , from, to);
	return MakeNewsArray(env,news);
}
